﻿using System;
using System.Data;
using System.Data.SqlClient;
using Microsoft.SqlServer.Server;

namespace DataCenter.Share
{
    public class BinaryRecord
    {
        private int objectId;
        private int valueType;
        private DateTime valueTime;
        private Binary binaryValue;
        private DateTime importTime0 = Utils.EMPTY_TIME;
        private DateTime importTime = Utils.EMPTY_TIME;
        private DateTime interpolateTime0 = Utils.EMPTY_TIME;
        private DateTime interpolateTime = Utils.EMPTY_TIME;
        private bool newRecord = true;
        //private bool isSubmit = false;

        public int ObjectId
        {
            get { return objectId; }
            set { objectId = value; }
        }

        public int ValueType
        {
            get { return valueType; }
            set { valueType = value; }
        }

        public DateTime ValueTime
        {
            get { return valueTime; }
            set { valueTime = value; }
        }

        public Binary BinaryValue
        {
            get { return binaryValue; }
            set
            {
                binaryValue = value;
                if (binaryValue != null)
                    binaryValue.ValueTime = valueTime;
            }
        }

        public DateTime ImportTime0
        {
            get { return importTime0; }
        }

        public DateTime ImportTime
        {
            get { return importTime; }
            set { importTime = value; }
        }

        public DateTime InterpolateTime0
        {
            get { return interpolateTime0; }
        }

        public DateTime InterpolateTime
        {
            get { return interpolateTime; }
            set { interpolateTime = value; }
        }

        public bool NewRecord
        {
            get { return newRecord; }
            set { newRecord = value; }
        }

        public bool IsSubmit
        {
            //get { return isSubmit; }
            get
            {
                return (importTime != importTime0 || interpolateTime != interpolateTime0);
            }
            //set { isSubmit = value; }
        }

        public int Interval
        {
            get { return binaryValue.Interval; }
        }

        public int FieldCount
        {
            get { return binaryValue.FieldCount; }
        }

        public int RecordCount
        {
            get { return binaryValue.RecordCount; }
        }

        public DateTime UpdateImportTime()
        {
            if (valueTime == Utils.TODAY)
            {
                // 如果为当天数据，则取数据的最后一个真实数据的时间
                importTime = binaryValue.GetLastTime(Utils.REAL_FLAG);
            }
            else
            {
                // 如果为历史数据，则取当前的23:59分
                importTime = Utils.GetLastTime(valueTime);
            }
            return importTime;
        }

        public DateTime UpdateInterpolateTime()
        {
            if (valueTime == Utils.TODAY)
            {
                // 如果为当天数据，则取数据的最后一个有效时间
                interpolateTime = binaryValue.GetLastTime();
            }
            else
            {
                // 如果为历史数据，则取当前的23:59分
                interpolateTime = Utils.GetLastTime(valueTime);
            }
            return interpolateTime;
        }

        public void Build(SqlDataReader reader)
        {
            object oValue = reader["ObjectId"];
            if (oValue == DBNull.Value || string.IsNullOrEmpty(oValue.ToString()))
                throw new NullReferenceException("字段\"ObjectId\"为空。");
            if (!int.TryParse(oValue.ToString(), out objectId))
                throw new InvalidCastException("字段\"ObjectId\"的值\"" + oValue + "\"不正确。");

            oValue = reader["ValueTime"];
            if (oValue == DBNull.Value || string.IsNullOrEmpty(oValue.ToString()))
                throw new NullReferenceException("字段\"ValueTime\"为空。");
            if (!DateTime.TryParse(oValue.ToString(), out valueTime))
                throw new InvalidCastException("字段\"ValueTime\"的值\"" + oValue + "\"不正确。");

            oValue = reader["ValueType"];
            if (oValue == DBNull.Value || string.IsNullOrEmpty(oValue.ToString()))
                throw new NullReferenceException("字段\"ValueType\"为空。");
            if (!int.TryParse(oValue.ToString(), out valueType))
                throw new InvalidCastException("字段\"ValueType\"的值\"" + oValue + "\"不正确。");

            oValue = reader["ImportTime"];
            if (oValue == DBNull.Value || string.IsNullOrEmpty(oValue.ToString()))
                throw new NullReferenceException("字段\"ImportTime\"为空。");
            if (!DateTime.TryParse(oValue.ToString(), out importTime))
                throw new InvalidCastException("字段\"ImportTime\"的值\"" + oValue + "\"不正确。");
            importTime0 = importTime;

            oValue = reader["InterpolateTime"];
            if (oValue == DBNull.Value || string.IsNullOrEmpty(oValue.ToString()))
                throw new NullReferenceException("字段\"InterpolateTime\"为空。");
            if (!DateTime.TryParse(oValue.ToString(), out interpolateTime))
                throw new InvalidCastException("字段\"InterpolateTime\"的值\"" + oValue + "\"不正确。");
            interpolateTime0 = interpolateTime;

            newRecord = false;
        }

        public void Build(SqlDataReader reader, bool isCache)
        {
            GetBinaryValue(reader);
            if (isCache) binaryValue.EndEdit();
        }

        public void Build(SqlDataReader reader, DateTime startTime, DateTime endTime, int columnIndex, bool isCache)
        {
            if (startTime.Date != valueTime.Date && endTime.Date != valueTime.Date)
            {
                // 不为开始时间和结束时间的日期
                GetBinaryValue(reader);
            }
            else
            {
                if (startTime.Date == valueTime.Date)
                {
                    if (endTime.Date == valueTime.Date)
                    {
                        // 开始时间和结束时间为同一天
                        binaryValue = new FragBinary(reader, startTime, endTime, columnIndex);
                    }
                    else
                    {
                        // 为开始时间
                        binaryValue = new FragBinary(reader, startTime, Utils.GetLastTime(endTime), columnIndex);
                    }
                }
                else
                {
                    // 为结束时间
                    binaryValue = new FragBinary(reader, endTime.Date, endTime, columnIndex);
                }
            }            
            binaryValue.ValueTime = valueTime;
            if (isCache) binaryValue.EndEdit();
        }

        public void CreateRecord(SqlCommand command)
        {
            Console.WriteLine("Insert: Id=" + objectId + ";Time=" + valueTime);

            TableSetting setting = TableSetting.GetInstance();
            setting.CreateTable(command, valueTime);
            string tableName = setting.GetTableName(valueTime);
            string commandString = @"INSERT INTO {0}(ObjectId, ValueTime, ValueType, BinaryValue, ImportTime, InterpolateTime)
                                    VALUES(@ObjectId, @ValueTime, @ValueType, @BinaryValue, @ImportTime, @InterpolateTime)";
            command.CommandText = string.Format(commandString, tableName);
            command.Parameters.Clear();
            command.Parameters.AddWithValue("@ObjectId", objectId);
            command.Parameters.AddWithValue("@ValueTime", valueTime);
            command.Parameters.AddWithValue("@ValueType", valueType);
            command.Parameters.AddWithValue("@BinaryValue", binaryValue.GetBuffer());
            command.Parameters.AddWithValue("@ImportTime", importTime);
            command.Parameters.AddWithValue("@InterpolateTime", interpolateTime);
            command.ExecuteNonQuery();
            command.Parameters.Clear();

            newRecord = false;
            importTime0 = importTime;
            interpolateTime0 = interpolateTime;

            UpdateRecentValue(command);
        }        

        public void UpdateRecord(SqlCommand command, DateTime startTime, DateTime endTime)
        {
            Console.WriteLine("Update: Id=" + objectId + ";StartTime=" + startTime + ";EndTime=" + endTime);

            byte[] buffer = binaryValue.GetBuffer(startTime, endTime);
            int offset = binaryValue.GetBufferOffset(startTime);
            int length = binaryValue.GetBufferLength(startTime, endTime);

            TableSetting setting = TableSetting.GetInstance();
            setting.CreateTable(command, startTime);
            string tableName = setting.GetTableName(valueTime);
            string commandString = @"UPDATE {0} SET BinaryValue.WRITE(@BinaryValue, @Offset, @Length), ImportTime = @ImportTime, InterpolateTime = @InterpolateTime
                                     WHERE ObjectId = @ObjectId AND ValueTime = @ValueTime AND ValueType = @ValueType";
            command.CommandText = string.Format(commandString, tableName);
            command.CommandType = CommandType.Text;
            command.Parameters.AddWithValue("@BinaryValue", buffer);
            command.Parameters.AddWithValue("@Offset", offset);
            command.Parameters.AddWithValue("@Length", length);
            command.Parameters.AddWithValue("@ObjectId", objectId);
            command.Parameters.AddWithValue("@ValueTime", valueTime);
            command.Parameters.AddWithValue("@ValueType", valueType); 
            command.Parameters.AddWithValue("@ImportTime", importTime);
            command.Parameters.AddWithValue("@InterpolateTime", interpolateTime);
            command.ExecuteNonQuery();
            command.Parameters.Clear();

            importTime0 = importTime;
            interpolateTime0 = interpolateTime;

            UpdateRecentValue(command);
        }

        public void UpdateRecord(SqlCommand command)
        {
            Console.WriteLine("Update; Id=" + objectId + ";Time=" + valueTime);

            byte[] buffer = binaryValue.GetBuffer();

            TableSetting setting = TableSetting.GetInstance();
            setting.CreateTable(command, valueTime);
            string tableName = setting.GetTableName(valueTime);
            string commandString = @"UPDATE {0} SET BinaryValue = @BinaryValue, ImportTime = @ImportTime, InterpolateTime = @InterpolateTime
                                     WHERE ObjectId = @ObjectId AND ValueTime = @ValueTime AND ValueType = @ValueType";
            command.CommandText = string.Format(commandString, tableName);
            command.CommandType = CommandType.Text;
            command.Parameters.AddWithValue("@BinaryValue", buffer);
            command.Parameters.AddWithValue("@ImportTime", importTime);
            command.Parameters.AddWithValue("@InterpolateTime", interpolateTime);
            command.Parameters.AddWithValue("@ObjectId", objectId);
            command.Parameters.AddWithValue("@ValueTime", valueTime);
            command.Parameters.AddWithValue("@ValueType", valueType);
            command.ExecuteNonQuery();
            command.Parameters.Clear();

            importTime0 = importTime;
            interpolateTime0 = interpolateTime;

            UpdateRecentValue(command);
        }

        public void UpdateRecordCount(SqlCommand command)
        {
            TableSetting setting = TableSetting.GetInstance();
            setting.CreateTable(command, valueTime);
            string tableName = setting.GetTableName(valueTime);
            string commandString = @"UPDATE {0} SET BinaryValue.WRITE(@RecordCount, 1, 4)
                                     WHERE ObjectId = @ObjectId AND ValueTime = @ValueTime AND ValueType = @ValueType";
            command.CommandText = string.Format(commandString, tableName);
            command.CommandType = CommandType.Text;
            command.Parameters.AddWithValue("@ObjectId", objectId);
            command.Parameters.AddWithValue("@ValueTime", valueTime);
            command.Parameters.AddWithValue("@ValueType", valueType);
            command.Parameters.AddWithValue("@RecordCount", BitConverter.GetBytes(binaryValue.RecordCount));
            command.ExecuteNonQuery();
            command.Parameters.Clear();
        }

        public void UpdateRecordTime(SqlCommand command)
        {
            Console.WriteLine("UpdateTime: Id=" + objectId + ";ValueTime=" + valueTime + 
                ";ImportTime=" + importTime + ";InterpolateTime=" + interpolateTime);

            TableSetting setting = TableSetting.GetInstance();
            setting.CreateTable(command, valueTime);
            string tableName = setting.GetTableName(valueTime);
            string commandString = @"UPDATE {0} SET ImportTime = @ImportTime, InterpolateTime = @InterpolateTime
                                     WHERE ObjectId = @ObjectId AND ValueTime = @ValueTime AND ValueType = @ValueType";
            command.CommandText = string.Format(commandString, tableName);
            command.CommandType = CommandType.Text;
            command.Parameters.AddWithValue("@ObjectId", objectId);
            command.Parameters.AddWithValue("@ValueTime", valueTime);
            command.Parameters.AddWithValue("@ValueType", valueType);
            command.Parameters.AddWithValue("@ImportTime", importTime);
            command.Parameters.AddWithValue("@InterpolateTime", interpolateTime);
            command.ExecuteNonQuery();
            command.Parameters.Clear();

            importTime0 = importTime;
            interpolateTime0 = interpolateTime;
        }

        public void UpdateRecentValue(SqlCommand command)
        {
            TimePoint lastPoint = binaryValue.GetLastPoint(Utils.REAL_FLAG);
            if (lastPoint != null)
            {
                RecentValueManager manager = RecentValueManager.GetInstance();
                manager.UpdateRecentValue(command, objectId, valueType, lastPoint.Time, lastPoint.Value0);
            }
        }

        public void FillDataRecord(SqlDataRecord record, int step, int[] operTypes)
        {
            ((IIterator)binaryValue).Step = step;
            ((IIterator)binaryValue).OperTypes = operTypes;

            record.SetInt32(0, objectId);
            record.SetInt32(1, valueType);
            foreach (ResultRecordRow row in binaryValue)
            {                
                record.SetDateTime(2, row.ValueTime);
                record.SetInt32(3, row.OperType);
                for (int i = 0; i < FieldCount; i++)
                {
                    float? resultValue = row.Values[i];
                    if (resultValue.HasValue) 
                        record.SetFloat(i + 4, resultValue.Value);
                    else record.SetDBNull(i + 4);
                }
                SqlContext.Pipe.SendResultsRow(record);
            }
        }

        private void GetBinaryValue(SqlDataReader reader)
        {
            object oValue = reader["BinaryValue"];
            if (oValue == DBNull.Value)
                throw new NullReferenceException("字段\"BinaryValue\"为空。");
            byte[] buffer = (byte[])oValue;
            binaryValue = new DayBinary(buffer);
            binaryValue.ValueTime = valueTime;
        }
    }
}
